package deformation;

import stegru.tools.multiThreat.ParallelFor;
import grid.Grid;
import grid.GridBuilder;
import grid.GridPoint;
import handler.Handler;

/**
 * @author Stephan
 *
 *Implemented version of Deformation 
 * @param <T>
 */
public abstract class AbstractDeformation<T extends PreCompute> implements Deformation{
	
	/**
	 * 
	 * @param v src GridPoint
	 * @param precomputed the value that is returned by preComputeGridPoint(GridPoint v) @see {@link AbstractDeformation#preComputeGridPoint(GridPoint)}
	 * @return the deformed grid point
	 */
	public abstract GridPoint calculateGridPoint(GridPoint v, T precomputed);
	
	public abstract Handler getHandler();

	/**
	 * returns data that can be computed before the movedpoints are known
	 * @param v
	 * @return PreCompute data
	 */
	public abstract T preComputeGridPoint(GridPoint v);

	/**
	 * 
	 * @see deformation.Deformation#generateMorphingGrid(grid.Grid)
	 */
	@Override
	public MorphingGrid generateMorphingGrid(final Grid grid) {
		@SuppressWarnings({ "unchecked" })
		final T[] precomputed =  (T[]) new PreCompute[grid.size()];
		(new ParallelFor(0, grid.size()-1) {
			@Override
			protected void loop(int i) {
				precomputed[i] = preComputeGridPoint(grid.get(i));
			}
		}).calculate();
		return new MorphingGrid_Impl<T>(grid, getHandler(), precomputed, this);
	}
	
	/**
	 *  @see deformation.Deformation#calculate(Grid)
	 */
	public Grid calculate(final Grid grid) {
		final GridBuilder gBuild = grid.getBuilder();
		@SuppressWarnings("unchecked")
		final T[] precomputed =  (T[]) new PreCompute[grid.size()];
		(new ParallelFor(0, grid.size()-1) {
			@Override
			protected void loop(int i) {
				precomputed[i] = preComputeGridPoint(grid.get(i));
			}
		}).calculate();
		(new ParallelFor(0, grid.size()-1) {
			@Override
			protected void loop(int i) {
				gBuild.set(i, calculateGridPoint(grid.get(i), precomputed[i]));
			}
		}).calculate();		
	return gBuild.generate();
	}
	


	protected Grid calculate(final Grid grid, final T[] precomputed) {
		final GridBuilder gBuild = grid.getBuilder();
		(new ParallelFor(0, grid.size()-1) {
			@Override
			protected void loop(int i) {
				gBuild.set(i, calculateGridPoint(grid.get(i), precomputed[i]));
			}
		}).calculate();		
	return gBuild.generate();
	}
	

}
